package base

import (
	"database/sql"
	"encoding/base64"
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/shirou/gopsutil/v4/mem"
	"go-api-template/api/http"
	"go-api-template/api/models/system"
	"go-api-template/api/models/tool"
	"go-api-template/internal/e"
	"go-api-template/internal/util/stringUtil"
	"go-api-template/internal/util/systemUtil"
	tokenUtil2 "go-api-template/internal/util/tokenUtil"
	"go-api-template/service"
)

func (req *CreateSortLinkReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var sl tool.SortLink
	// ID自增后，转成62进制，在DB保存映射关系
	sl.Url = req.Url
	err = s.Db.Create(&sl).Scan(&sl).Error
	if err == nil {
		var bytes []byte
		id := sl.ID
		for id > 0 {
			bytes = append(bytes, chars[sl.ID%62])
			id = id / 62
		}
		reverseByteSlice(bytes)
		sl.K = string(bytes)
		s.Db.Debug().Model(&sl).Where("id = ?", sl.ID).Update("k", sl.K)
	}

	resp.SetData(map[string]interface{}{
		"link": "http://127.0.0.1:8080/api/base/t/" + sl.K,
	})
	return
}

func (req *LoginReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var u system.User
	var data LoginResp

	switch req.Mode {
	case LoginReqModeAccount, LoginReqModePhone:
		if req.Mode == LoginReqModeAccount {
			if err = s.Db.Where("username = ?", req.Username).Or("account = ?", req.Username).Find(&u).Error; err != nil {
				return
			}
		} else {
			if err = s.Db.Where("phone = ?", req.Phone).Find(&u).Error; err != nil {
				return
			}
		}

		if checkResp, checkErr, ok := req.checkUser(u); !ok {
			return checkResp, checkErr
		}
		if checkResp, checkErr, ok := req.checkPassword(u); !ok {
			return checkResp, checkErr
		}

	case LoginReqModePhoneCaptcha, LoginReqModeEmailCaptcha:
		if req.Mode == LoginReqModePhoneCaptcha {
			if err = s.Db.Where("phone = ?", req.Phone).Find(&u).Error; err != nil {
				return
			}
		} else {
			if err = s.Db.Where("email = ?", req.Email).Find(&u).Error; err != nil {
				return
			}
		}

		if checkResp, checkErr, ok := req.checkUser(u); !ok {
			return checkResp, checkErr
		}

		if req.Mode == LoginReqModePhoneCaptcha {
			if checkResp, checkErr, ok := req.checkCaptcha(req.Phone, req.Captcha); !ok {
				return checkResp, checkErr
			}
		} else {
			if checkResp, checkErr, ok := req.checkCaptcha(req.Email, req.Captcha); !ok {
				return checkResp, checkErr
			}
		}

	default:

	}

	// 设置token
	t, err := tokenUtil2.JwtGenerateToken(tokenUtil2.UserStd{UserId: u.ID, IsAdmin: u.IsAdmin == 1}, time.Duration(s.Cf.App.Token.ExpirationTime)*time.Minute)
	if err != nil {
		resp.SetCode(e.ServerFailed)
	}

	// 设置Cookie
	// ctx.SetCookie("token", t, 0, "/", ctx.GetHeader("Host"), false, true)

	// 缓存token（用于单点登录和续期）
	err = service.Cache.Set(e.UserTokenCache+u.ID, t, time.Duration(2*s.Cf.App.Token.ExpirationTime)*time.Minute)
	if err != nil {
		return
	}

	data.Token = t
	data.UserInfo.Name = u.Username
	data.UserInfo.UserId = u.ID

	// 登录次数清零
	if err := service.Cache.Set(e.UserLoginLimitCache+ctx.ClientIP(), 0, 60*time.Second); err != nil {
		service.Log.Error(err.Error())
	}

	resp.SetData(data)

	return
}

func (req *LoginReq) checkUser(u system.User) (resp http.Resp, err error, ok bool) {
	// 用户不存在
	if u.ID == "" {
		resp.SetCode(e.UserNotFound)
		return
	}

	return resp, err, true
}

func (req *LoginReq) checkPassword(u system.User) (resp http.Resp, err error, ok bool) {
	// 密码错误
	if u.Password != base64.StdEncoding.EncodeToString([]byte(req.Password)) {
		resp.SetCode(e.PasswordFailed)
		return
	}
	return resp, err, true
}

func (req *LoginReq) checkCaptcha(k string, captcha string) (resp http.Resp, err error, ok bool) {
	getCaptcha, _ := service.Cache.Get("captcha:" + k)
	if getCaptcha == nil {
		resp.SetCode(e.CaptchaExpired)
		return
	}
	if getCaptcha.(string) != captcha {
		resp.SetCode(e.CaptchaFailed)
		return
	}
	return resp, err, true
}

func (req *CurrentReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var u system.User
	var data CurrentResp
	var menuIds []string

	// 获取用户信息
	s.Db.Model(&u).Where("id = ?", req.UserId).Scan(&u)
	if u.ID == "" {
		resp.SetCode(e.UserIsDisable)
		return
	}
	_ = copier.Copy(&data, &u)

	// 直接授权的菜单
	filteredPolicy := s.RBAC.GetFilteredPolicy(0, req.UserId, "", "menu")
	for _, policy := range filteredPolicy {
		resource := policy[1] // 假设资源是策略的第二个元素
		menuIds = append(menuIds, resource)
	}

	// 授权角色的菜单
	roleIds, _ := s.RBAC.GetRolesForUser(req.UserId)
	fmt.Println("roleIds: ", roleIds)
	for _, roleId := range roleIds {
		rolePolicy := s.RBAC.GetFilteredPolicy(0, roleId, "", "menu")
		for _, policy := range rolePolicy {
			resource := policy[1] // 假设资源是策略的第二个元素
			menuIds = append(menuIds, resource)
		}
	}

	s.Db.Where("id in ?", menuIds).Find(&data.Menus)

	resp.SetData(data)
	return
}

func (req *SendCaptchaReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {
	var data SendCaptchaResp

	if code, err_ := stringUtil.GenerateSecureRandomNumberCode(6); err_ != nil {
		return http.Resp{}, err_
	} else {
		switch req.Mode {
		case SendCaptchaReqModePhone:
			if err_ = s.Cache.Set(fmt.Sprintf("captcha:%s", req.Phone), code, 5*time.Minute); err_ != nil {
				return http.Resp{}, err_
			}
		case SendCaptchaReqModeEmail:
			if err_ = s.Cache.Set(fmt.Sprintf("captcha:%s", req.Email), code, 5*time.Minute); err_ != nil {
				return http.Resp{}, err_
			}
		}
		// 发送验证码
		fmt.Printf("phone: %s ,code: %s \n", req.Phone, code)
	}

	resp.SetData(data)
	return
}

func (req *StatusReq) Do(ctx *gin.Context, s *service.Service) (resp http.Resp, err error) {

	memInfo, err := mem.VirtualMemory()
	if err != nil {
		return
	}

	var data = StatusResp{

		Runtime: StatusRespRuntime{
			UseMem: memInfo.Used / 1000 / 1000,
		},
		System: StatusRespSystem{
			CpuPercent:  systemUtil.GetCpuPercent(),
			MemPercent:  systemUtil.GetMemPercent(),
			DiskPercent: systemUtil.GetDiskPercent(),
		},
		Service: StatusRespService{
			Mysql: "OK",
			Redis: "OK",
		},
	}
	if service.Conf.Db.Use == "mysql" {
		if _, err_ := sql.Open("mysql", service.Conf.Db.Dsn); err_ != nil {
			data.Service.Mysql = fmt.Sprintf("connection error: %s\n", err_.Error())
		}
	}

	if service.Conf.Cache.Use == "redis" {
		if _, err_ := service.Redis.Ping().Result(); err_ != nil {
			data.Service.Redis = fmt.Sprintf("connection error: %s\n", err_.Error())
		}
	}

	resp.SetData(data)
	return
}
